package main

import (
	"fmt"
	"oslab/model"
	"sort"
)

var (
	rq        = make([]*model.Pcb, 0)
	list      = make([]*model.Pcb, 0)
	fq        = make([]*model.Pcb, 0)
	timeSlice = 0
	curTime   = 0
)

// highResponseRatio 高响应比优先
// 每次进行调度时先计算响应比优先级，然后把响应比优先级最高的进程投入运行
// 当等待时间相同时，要求服务时间越短，响应比越高，短作业容易被选中执行
// 当要求服务比相同时，等待时间越长，响应比越高，兼顾长作业进程
//
// test:
// 	model.NewPcb("A", 0, 6))
//	model.NewPcb("B", 3, 2))
//	model.NewPcb("C", 4, 4))
//	model.NewPcb("D", 4, 1))
// result:
// 	{A 0 0 6 6 6 1 6}
//	{D 4 6 1 7 3 3 1}
//	{B 3 7 2 9 6 3 2}
//	{C 4 9 4 13 9 2.25 4}
func highResponseRatio(list []*model.Pcb) {
	// 记录上一个完成作业的时间
	finish := 0
	sort.Slice(list, func(i, j int) bool {
		return list[i].InTime < list[j].InTime
	})

	for len(list) > 0 || len(rq) > 0 {
		for len(list) != 0 {
			if curTime < list[0].InTime && len(rq) == 0 {
				curTime = list[0].InTime
			}
			if list[0].InTime <= curTime {
				fmt.Println(list[0].Name + " W")
				rq = append(rq, list[0])
				list = list[1:]
			} else {
				break
			}
		}
		sort.Slice(rq, func(i, j int) bool {
			return (float32(abs(finish-rq[i].InTime)) / float32(rq[i].WorkTime)) > (float32(abs(finish-rq[j].InTime)) / float32(rq[j].WorkTime))
		})

		// 调度
		curQueue := rq[0]
		fmt.Println(curQueue.Name + " R")
		rq = rq[1:]
		// 设置开始时间
		curQueue.StartTime = curTime
		// 调度完整个p，更新当前时间
		curTime = curTime + curQueue.WorkTime
		// 更新完成时间，周转时间，带权周转时间
		curQueue.FinishTime = curTime
		fmt.Println(curQueue.Name + " F")
		finish = curTime
		curQueue.TurnTime = curTime - curQueue.InTime
		curQueue.PowerTurnTime = float32(curQueue.TurnTime) / float32(curQueue.WorkTime)
		fq = append(fq, curQueue)
	}
	s := float32(0)
	for i := 0; i < len(fq); i++ {
		fmt.Println(*fq[i])
		s += (*fq[i]).PowerTurnTime
	}
	fmt.Println(s / float32(len(fq)))

}

func abs(a int) int {
	if a < 0 {
		return -a
	}
	return a
}

// shortAssignmentsPreferSchedule 短作业优先
// test:
// 	model.NewPcb("A", 0, 10))
// 	model.NewPcb("B", 5, 1))
// 	model.NewPcb("C", 3, 7))
// 	model.NewPcb("D", 9, 5))
// result:
// 	{A 0 0 10 10 10 1 10}
// 	{B 5 10 1 11 6 6 1}
// 	{D 9 11 5 16 7 1.4 5}
// 	{C 3 16 7 23 20 2.857143 7}
func shortAssignmentsPreferSchedule(list []*model.Pcb) {

	sort.Slice(list, func(i, j int) bool {
		return list[i].InTime < list[j].InTime
	})

	// 检索可能到达的任务
	for len(list) > 0 || len(rq) > 0 {
		// 可以是true，要避免list为空的情况
		for len(list) != 0 {
			if len(rq) == 0 && curTime < list[0].InTime {
				curTime = list[0].InTime
			}
			if list[0].InTime <= curTime {
				fmt.Println(list[0].Name + " W")
				rq = append(rq, list[0])
				list = list[1:]
			} else {
				// 剪枝
				break
			}
		}

		// 其实整个地方是应该用插入排序的
		sort.Slice(rq, func(i, j int) bool {
			return rq[i].WorkTime < rq[j].WorkTime
		})
		// 调用rq
		curQueue := rq[0]
		fmt.Println(curQueue.Name + " R")

		rq = rq[1:]
		// 设置开始时间
		curQueue.StartTime = curTime
		// 调度完整个p，更新当前时间
		curTime = curTime + curQueue.WorkTime
		// 更新完成时间，周转时间，带权周转时间
		curQueue.FinishTime = curTime
		fmt.Println(curQueue.Name + " F")
		curQueue.TurnTime = curTime - curQueue.InTime
		curQueue.PowerTurnTime = float32(curQueue.TurnTime) / float32(curQueue.WorkTime)
		fq = append(fq, curQueue)
	}
	s := float32(0)
	for i := 0; i < len(fq); i++ {
		fmt.Println(*fq[i])
		s += (*fq[i]).PowerTurnTime
	}
	fmt.Println(s / float32(len(fq)))
}

// timeSliceSortSchedule 时间片轮转
// test:
// 	model.NewPcb("A", 0, 9))
// 	model.NewPcb("B", 1, 6))
// 	model.NewPcb("C", 2, 4))
// 	model.NewPcb("D", 3, 5))
// 	model.NewPcb("E", 4, 1))
// result:
// 	&{E 4 10 1 11 7 7 1 0}
// 	&{C 2 4 4 15 13 3.25 2 0}
// 	&{B 1 2 6 21 20 3.3333333 2 0}
// 	&{D 3 8 5 24 21 4.2 1 0}
// 	&{A 0 0 9 25 25 2.7777777 1 0}
func timeSliceSortSchedule(list []*model.Pcb, ts int) {
	c := len(list)
	timeSlice = ts
	s := float32(0)
	// 取样本队列第一个开始
	if len(list) != 0 {
		fp := list[0]
		list = list[1:]
		fmt.Println(fp.Name + " W")
		rq = append(rq, fp)
		curTime = fp.InTime
	}

	for len(rq) != 0 || len(list) != 0 {
		// 从样本中获取
		if len(rq) == 0 {
			fp := list[0]
			list = list[1:]
			fmt.Println(fp.Name + " W")
			rq = append(rq, fp)
			curTime = fp.InTime
		}

		cp := rq[0]
		rq = rq[1:]
		fmt.Println(cp.Name + " R")

		if cp.StartTime == 0 && cp.InTime != 0 {
			cp.StartTime = curTime
		}
		// 进程在时间片上结束
		if cp.Time-timeSlice <= 0 {
			curTime += cp.Time
			fmt.Println(cp.Name + " F")

			cp.FinishTime = curTime
			cp.TurnTime = curTime - cp.InTime
			cp.PowerTurnTime = float32(cp.TurnTime) / float32(cp.WorkTime)
			// schedule
			cnt := 0
			for cnt < len(list) {
				p := list[cnt]
				// 入队进程到达时间必须小于等于当前时间
				if p.InTime <= curTime {
					rq = append(rq, p)
					list = append(list[:cnt], list[cnt+1:]...)
					fmt.Println(p.Name + " W")
				} else {
					cnt++
				}
			}
			s += cp.PowerTurnTime
			fmt.Println(cp)
		} else {
			// 时间片不够
			curTime += timeSlice
			cp.Time = cp.Time - timeSlice

			cnt := 0
			// 进入就绪队列
			for cnt < len(list) {
				p := list[cnt]
				if p.InTime <= curTime {
					rq = append(rq, p)
					list = append(list[:cnt], list[cnt+1:]...)
					fmt.Println(p.Name + " W")

				} else {
					cnt++
				}
			}
			fmt.Println(cp.Name + " W")
			rq = append(rq, cp)
		}
	}
	fmt.Println(s / float32(c))
}
